import groovy.sql.Sql
import javax.mail.*
import javax.mail.internet.*

Double resnik(Set s1, Set s2, Map icmap) {
  def s = s1.intersect(s2)
  def max = 0.0
  s.each {
    if (icmap[it] > max) {
      max = icmap[it]
    }
  }
  return max
}

Double jaccard(Set s1, Set s2) {
  if (s2.size() < s1.size()) { // ensure s1 is the smaller set
    def s = s1
    s1 = s2
    s2 = s
  }
  
  def inter = 0
  def un = 0
  s1.each { 
    if (s2.contains(it)) {
      inter += 1
    }
    un += 1
  }
  un += s2.size() - inter

  if (un == 0.0) {
    return 0.0
  } else {
    return inter/un
  }
}

Double simGIC(Set s1, Set s2, Map icmap) {
  if (s2.size() < s1.size()) { // ensure s1 is the smaller set
    def s = s1
    s1 = s2
    s2 = s
  }
  
  def inter = 0.0
  def un = 0.0
  s1.each { 
    if (s2.contains(it)) {
      inter += icmap[it]
    }
    un += icmap[it]
  }
  s2.each { un += icmap[it] }
  un -= inter
  if (un == 0.0) {
    return 0.0
  } else {
    return inter/un
  }
}

void similarity(Map supermap, Map analmap1, Map analmap2, Integer max, PrintWriter fout, String type) {

  /* compute semantic closure of annotations */
  def processedanalmap1 = [:]
  def processedanalmap2 = [:]
  analmap1.each { k, v ->
    processedanalmap1[k] = new TreeSet()
    v.each { cl ->
      processedanalmap1[k].add(cl)
      supermap[cl]?.each { processedanalmap1[k].add(it) }
    }
  }
  analmap2.each { k, v ->
    processedanalmap2[k] = new TreeSet()
    v.each { cl ->
      processedanalmap2[k].add(cl)
      supermap[cl]?.each { processedanalmap2[k].add(it) }
    }
  }
  /* calculate information content of entities in analmap */
  Map<String, Double> infocontent = [:]
  def size1 = analmap1.keySet().size()
  def size2 = analmap2.keySet().size()
  def sizeinter = analmap1.keySet().intersect(analmap2.keySet()).size()
  def size = size1+size2-sizeinter
  def countmap = [:]
  def icmap = [:]
  processedanalmap1.each { k, v ->
    v.each {
      if (countmap[it] == null) {
	countmap[it] = 1
      } else {
	countmap[it] += 1
      }
    }
  }
  processedanalmap2.each { k, v ->
    v.each {
      if (countmap[it] == null) {
	countmap[it] = 1
      } else {
	countmap[it] += 1
      }
    }
  }
  countmap.each { k, v ->
    icmap[k] = - Math.log(v/size) / Math.log(2)
  }

  /* calculate similarity */
  processedanalmap1.each {k1, v1 ->
    def queue = new PriorityQueue() // queue is a bounded (limited size) priority queue of SimPairs
    processedanalmap2.each {k2, v2 ->
      if (k1!=k2) {
	SimPair pair = new SimPair(k2, 0.0)
	switch(type) {
	case "jaccard":
	  pair.val = jaccard(v1, v2)
	  break ;
	case "simGIC":
	  pair.val = simGIC(v1, v2, icmap)
	  break ;
	case "resnik":
	  pair.val = resnik(v1, v2, icmap)
	  break ;
	}
	queue.add(pair)
	if (queue.size()>max) {
	  queue.poll()
	}
      }
    }
    while (queue.size()>0) {
      def it = queue.poll()
      fout.println(k1+"\t"+it.term+"\t"+it.val)
    }
  }
  fout.flush()
}

sql = Sql.newInstance("jdbc:mysql://localhost:3306/ontosim?connectTimeout=0&socketTimeout=0&autoReconnect=true", "root","", "com.mysql.jdbc.Driver")

if (sql.rows("SELECT * FROM Job WHERE status=1").size()>0) { // already a job running
  System.exit(0)
}

def superfile = "/mnt/ontofunc/ontosim/superclasses.txt"

def datadir = "/mnt/ontofunc/ontosim/data/"
def outdatadir = "/mnt/ontofunc/ontosim/dataout/"

def supermap = null

def initSuperMap = {
  new File(superfile).splitEachLine("\t") { line ->
    if (line.size()>1) {
      def oid = line[0]
      if (supermap[oid] == null) {
	supermap[oid] = new TreeSet()
      }
      supermap[oid].add(oid)
      line[1..-1].each { supermap[oid].add(it) }
    }
  }
}

def analmap1 = null
def analmap2 = null
def type = null
def query = "SELECT * FROM Job WHERE status=0 ORDER BY id LIMIT 1" 
def maxcomp = null
def email = null
def id = null
def fout = null
def processed = false
try {
  sql.eachRow(query) { 
    processed = true
    if (supermap == null) {
      supermap = [:]
      initSuperMap()
    }
    
    id = it.id
    def infile = datadir+id+"/input.txt"
    
    analmap1 = [:]
    analmap2 = [:]
    new File(infile).splitEachLine("\t") { line ->
      def oid = line[0]
      def anno = line[1]
      def numb = line[2]
      if (numb == "0") {
	if (analmap1[oid] == null) {
	  analmap1[oid] = new TreeSet()
	}
	analmap1[oid].add(anno)
      } else {
	if (analmap2[oid] == null) {
	  analmap2[oid] = new TreeSet()
	}
	analmap2[oid].add(anno)
      }
    }
    
    def outdir = outdatadir+id
    new File(outdir).mkdir()
    def outfile = new File(outdir+"/results.txt")
    fout = new PrintWriter(new BufferedWriter(new FileWriter(outfile)))
    def ontologies = it.ontologies
    maxcomp = new Integer(it.maxcomp)
    email = it.email
    type = it.test
  }
  if (processed) {
    sql.execute("UPDATE Job SET status=1 WHERE id=$id")
    
    similarity(supermap, analmap1, analmap2, maxcomp, fout, type)
    
    sql.execute("UPDATE Job SET status=2 WHERE id=$id") // executed ok
    
    def properties = new Properties ( ) 
    properties.put ( 'mail.smtp.host' , 'smtphost.aber.ac.uk' ) 
    def session = Session.getInstance ( properties ) 
    try { 
      def message = new MimeMessage ( session ) 
      message.from = new InternetAddress ( 'roh25@aber.ac.uk' )
      message.setRecipient ( Message.RecipientType.TO , new InternetAddress ( email ) )
      message.subject = "Your OntoSIM Job with id $id has finished" 
      message.sentDate = new Date ( ) 
      message.text = "Hi. Your OntoSIM job with id $id has finished. To view your results, please visit http://phenomebrowser.net/ontosim/checkstatus.php?id=$id. For questions and help, please respond to this email.\n\nRobert Hoehndorf"
      Transport.send ( message )
    }
    catch ( final MessagingException me ) {
      me.printStackTrace ( )
    }
  }
} catch (Exception E) {
  sql.execute("UPDATE Job SET status=3 WHERE id=$id")
}

